﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RoketPack.Management.Detection;
using RoketPack.Management.Objects;

namespace RoketPack.Management
{
    /// <summary>
    /// This class represents a downloadable component of an application.
    /// </summary>
    class Component : MarshalByRefObject
    {
        private string p_ID = null;
        private string p_Name = null;
        private bool p_IsAvailable;
        private bool p_IsRecommended;

        private List<Link> p_Links = new List<Link>();
        private List<File> p_Files = new List<File>();
        private List<Folder> p_Folders = new List<Folder>();
        private List<Variable> p_Variables = new List<Variable>();
        private List<Detector> p_Detectors = new List<Detector>();
        private Classic p_ClassicInstaller = null;

        private bool p_PermitModification = false;

        private Component()
        {
        }

        /// <summary>
        /// Create a new instance of Component, with no settings applied.  The data in this
        /// application can be modified via properties until PermitModification is set to true.
        /// </summary>
        /// <returns>The new Component instance.</returns>
        internal static Component FetchEmpty()
        {
            Component c = new Component();
            c.p_PermitModification = true;
            return c;
        }

        /// <summary>
        /// Returns whether or not the component is currently installed.
        /// </summary>
        /// <returns>Whether or not the component is currently installed.</returns>
        internal bool IsInstalled()
        {
            if (this.p_Detectors.Count == 0)
                return false;

            foreach (Detector d in this.p_Detectors)
            {
                if (!d.Matched())
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Returns whether or not one or more files, folders or links is not up
        /// to date within the component.  This is used to detect when the software
        /// manager should offer the user the ability to update the software.
        /// </summary>
        /// <returns></returns>
        internal bool IsOutOfDate()
        {
            if (this.p_ClassicInstaller != null)
            {
                if (this.p_ClassicInstaller.InstalledVersion < this.p_ClassicInstaller.AvailableVersion)
                    return true;
            }
            foreach (File f in this.p_Files)
            {
                if (f.InstalledVersion < f.AvailableVersion || f.DeleteOnNextUpdate)
                    return true;
            }
            foreach (Folder f in this.p_Folders)
            {
                if (f.InstalledVersion < f.AvailableVersion || f.DeleteOnNextUpdate)
                    return true;
            }
            foreach (Link l in this.p_Links)
            {
                if (l.InstalledVersion < l.AvailableVersion || l.DeleteOnNextUpdate)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Forces the installed version to a particular value.
        /// </summary>
        /// <param name="version">The value to to force the version to.</param>
        internal void ForceInstalledVersion(File file, int version)
        {
            if (!this.p_Files.Contains(file))
                throw new ArgumentException("The File object specified as an argument is not part of this component.");

            file.InstalledVersion = version;
        }

        /// <summary>
        /// Forces the installed version to a particular value.
        /// </summary>
        /// <param name="version">The value to to force the version to.</param>
        internal void ForceInstalledVersion(Folder folder, int version)
        {
            if (!this.p_Folders.Contains(folder))
                throw new ArgumentException("The Folder object specified as an argument is not part of this component.");

            folder.InstalledVersion = version;
        }

        /// <summary>
        /// Forces the installed version to a particular value.
        /// </summary>
        /// <param name="version">The value to to force the version to.</param>
        internal void ForceInstalledVersion(Link link, int version)
        {
            if (!this.p_Links.Contains(link))
                throw new ArgumentException("The Link object specified as an argument is not part of this component.");

            link.InstalledVersion = version;
        }

        /// <summary>
        /// Forces the installed version to a particular value.
        /// </summary>
        /// <param name="version">The value to to force the version to.</param>
        internal void ForceInstalledVersion(Classic classic, int version)
        {
            if (this.p_ClassicInstaller != classic)
                throw new ArgumentException("The Classic (installer) object specified as an argument is not assigned to this component.");

            classic.InstalledVersion = version;
        }

        /// <summary>
        /// The ID of the component within the application.
        /// </summary>
        internal string ID
        {
            get
            {
                return this.p_ID;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_ID = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        /// <summary>
        /// The name of the component.
        /// </summary>
        internal string Name
        {
            get
            {
                return this.p_Name;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Name = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        /// <summary>
        /// Whether the component is available for installation on the computer.
        /// </summary>
        internal bool Available
        {
            get
            {
                return this.p_IsAvailable;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_IsAvailable = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        /// <summary>
        /// Whether the component is recommended for installation.
        /// </summary>
        internal bool Recommended
        {
            get
            {
                return this.p_IsRecommended;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_IsRecommended = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        /// <summary>
        /// Whether the properties of the component can be modified.  This value can not be
        /// changed after it is set to false.
        /// </summary>
        internal bool PermitModification
        {
            get
            {
                return this.p_PermitModification;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_PermitModification = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }

        internal List<Link> Links
        {
            get
            {
                return this.p_Links;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Links = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        internal List<File> Files
        {
            get
            {
                return this.p_Files;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Files = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        internal List<Folder> Folders
        {
            get
            {
                return this.p_Folders;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Folders = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        internal List<Variable> Variables
        {
            get
            {
                return this.p_Variables;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Variables = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        internal List<Detector> Detectors
        {
            get
            {
                return this.p_Detectors;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_Detectors = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }

        internal Classic ClassicInstaller
        {
            get
            {
                return this.p_ClassicInstaller;
            }
            set
            {
                if (this.p_PermitModification)
                    this.p_ClassicInstaller = value;
                else
                    throw new ApplicationModificationNotPermittedException();
            }
        }
    }
}
